package org.papervision3d.materials.shaders;
import nme.display.BitmapData;
import nme.display.Graphics;
import nme.geom.Matrix;
import org.papervision3d.core.geom.renderables.Triangle3D;
//import flash.utils.Dictionary;
import nme.ObjectHash;

import org.papervision3d.core.log.PaperLogger;
import org.papervision3d.core.material.TriangleMaterial;
import org.papervision3d.core.render.command.RenderTriangle;
import org.papervision3d.core.render.data.RenderSessionData;
import org.papervision3d.core.render.draw.ITriangleDrawer;
import org.papervision3d.core.render.material.IUpdateAfterMaterial;
import org.papervision3d.core.render.material.IUpdateBeforeMaterial;
import org.papervision3d.core.render.shader.ShaderObjectData;
import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.objects.DisplayObject3D;
/**
 * @Author Ralph Hauwert
 */
class ShadedMaterial extends TriangleMaterial, implements ITriangleDrawer,implements IUpdateBeforeMaterial,implements IUpdateAfterMaterial
{
	private var _shaderCompositeMode:Int;
	
	public var shader:Shader;
	public var material:BitmapMaterial;
	public var shaderObjectData:ObjectHash<DisplayObject3D, ShaderObjectData>;
	
	public function new(material:BitmapMaterial, shader:Shader, compositeMode:Int = 0)
	{
		super();	
		this.shader = shader;
		this.material = material;
		shaderCompositeMode = compositeMode;
		init();
	}
	
	private function init():Void
	{
		shaderObjectData = new ObjectHash<DisplayObject3D, ShaderObjectData>();
	}
	
	/**
	 * Localized vars
	 */
		 
	 private static var bmp:BitmapData;
	override public function drawTriangle(tri:RenderTriangle, graphics:Graphics, renderSessionData:RenderSessionData, altBitmap:BitmapData = null, altUV:Matrix = null):Void
	{
		var sod:ShaderObjectData = untyped (shaderObjectData.get(tri.renderableInstance.instance));
		if(shaderCompositeMode == ShaderCompositeModes.PER_LAYER){
			//Render shader to layer.
			material.drawTriangle(tri, graphics, renderSessionData, sod.shaderRenderer.outputBitmap);
			shader.renderLayer(tri.triangle, renderSessionData, sod);
		}else if(shaderCompositeMode == ShaderCompositeModes.PER_TRIANGLE_IN_BITMAP){
			//Render shader per tri - TO FIX.
			bmp = sod.getOutputBitmapFor(tri.triangle);
			material.drawTriangle(tri, graphics, renderSessionData, bmp, sod.triangleUVS.get(tri.triangle) != null ? sod.triangleUVS.get(tri.triangle) : sod.getPerTriUVForDraw(tri.triangle));
			shader.renderTri(tri.triangle,renderSessionData,sod,bmp);
		}
	}
	
	public function updateBeforeRender(renderSessionData:RenderSessionData):Void
	{
		var sod:ShaderObjectData;
		for(sod in shaderObjectData){
			sod.shaderRenderer.inputBitmap = material.bitmap;
			if(shaderCompositeMode == ShaderCompositeModes.PER_LAYER){
				if(sod.shaderRenderer.resizedInput){
					sod.shaderRenderer.resizedInput = false;
					sod.uvMatrices = new ObjectHash<Triangle3D, Matrix>();
				}
				sod.shaderRenderer.clear();	
			}
			if(Std.is(shader, ILightShader)){
				var ls:ILightShader = untyped shader;
				ls.updateLightMatrix(sod,renderSessionData);
			}
		}	
	}
	
	public function updateAfterRender(renderSessionData:RenderSessionData):Void
	{
		var sod:ShaderObjectData;
		for(sod in shaderObjectData){
			shader.updateAfterRender(renderSessionData, sod);
			if(shaderCompositeMode == ShaderCompositeModes.PER_LAYER){
				sod.shaderRenderer.render(renderSessionData);
			}
		}
	}
	
	override public function registerObject(displayObject3D:DisplayObject3D):Void
	{
		super.registerObject(displayObject3D);
		shaderObjectData.set(displayObject3D, new ShaderObjectData(displayObject3D, material, this));
		var sod:ShaderObjectData = shaderObjectData.get(displayObject3D);
		sod.shaderRenderer.inputBitmap = material.bitmap;
		shader.setContainerForObject(displayObject3D,sod.shaderRenderer.getLayerForShader(shader));
	}
	
	override public function unregisterObject(displayObject3D:DisplayObject3D):Void
	{
		super.unregisterObject(displayObject3D);
		var sod:ShaderObjectData = shaderObjectData.get(displayObject3D);
		sod.destroy();
		//delete shaderObjectData[displayObject3D];
		shaderObjectData.remove(displayObject3D);
	}
	
	public var shaderCompositeMode(get_shaderCompositeMode, set_shaderCompositeMode):Int;
	private function set_shaderCompositeMode(compositeMode:Int):Int
	{
		_shaderCompositeMode = compositeMode;
		return compositeMode;
	}
	
	private function get_shaderCompositeMode():Int
	{
		return _shaderCompositeMode;
	}
	
	/**
	 * Debug thingy.
	 */
	public function getOutputBitmapDataFor(object:DisplayObject3D):BitmapData
	{
		if(shaderCompositeMode == ShaderCompositeModes.PER_LAYER){
			if(shaderObjectData.get(object) != null){
				var sod:ShaderObjectData = untyped shaderObjectData.get(object);
				return sod.shaderRenderer.outputBitmap;
			}else{
				PaperLogger.warning("object not registered with shaded material");
			}
		}else{
			PaperLogger.warning("getOutputBitmapDataFor only works on per layer mode");
		}
		return null;
	}
	
	
	
	override public function destroy():Void
	{
		super.destroy(); 
		var sod:ShaderObjectData;
		for(sod in shaderObjectData){
			sod.destroy();
		}
		material = null;
		shader = null;
	}
	
}
	
